/*------------------------------ - 程序介绍------------------------------*/
//版本：VS2017 + Opencv2.4.9
//描述：OpenCV学习之路——车牌识别之车牌定位
/*-----------------------------------------------------------------------*/

//https://www.cnblogs.com/thunder-wu/p/6985169.html
//#include "opencv.hpp"
#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2\core\core.hpp"
#include "vector"
#include "iostream"
#include "time.h"

using namespace cv;
using namespace std;

Mat histeq(Mat in)
{
    Mat out(in.size(), in.type());
    if (in.channels() == 3) {
        Mat hsv;
        vector<Mat> hsvSplit;
        //cvtColor(in, hsv, CV_BGR2HSV);
		cvtColor(in, hsv, COLOR_BGR2HSV);
        split(hsv, hsvSplit);
        equalizeHist(hsvSplit[2], hsvSplit[2]);
        merge(hsvSplit, hsv);
        //cvtColor(hsv, out, CV_HSV2BGR);
		cvtColor(hsv, out, COLOR_HSV2BGR);
    }
    else if (in.channels() == 1) {
        equalizeHist(in, out);
    }

    return out;

}

//! 对minAreaRect获得的最小外接矩形，用纵横比进行判断
bool verifySizes(RotatedRect mr)
{
    float error = 0.3;
    //Spain car plate size: 52x11 aspect 4,7272
    //China car plate size: 440mm*140mm，aspect 3.142857
    float aspect = 3.142857;
    //Set a min and max area. All other patchs are discarded
    int min= 1*aspect*1; // minimum area
    int max= 2000*aspect*2000; // maximum area
    //int min = 44 * 14 * m_verifyMin; // minimum area
    //int max = 44 * 14 * m_verifyMax; // maximum area
                                     //Get only patchs that match to a respect ratio.
    float rmin = aspect - aspect*error;
    float rmax = aspect + aspect*error;

    int area = mr.size.height * mr.size.width;
    float r = (float)mr.size.width / (float)mr.size.height;
    if (r < 1)
    {
        r = (float)mr.size.height / (float)mr.size.width;
    }

    if ((area < min || area > max) || (r < rmin || r > rmax))
    {
        return false;
    }
    else
    {
        return true;
    }
}

Mat Gaussian(Mat &img) {
    Mat out;
    GaussianBlur(img, out, Size(3, 3),
        0, 0, BORDER_DEFAULT);
    return out;

}

Mat Grayscale(Mat &img) {
    Mat out;
    //cvtColor(img, out, CV_RGB2GRAY);
	cvtColor(img, out, COLOR_BGR2GRAY);
    return out;
}

Mat Sobel(Mat &img) {
    Mat out;
    Mat grad_x, grad_y;
    Mat abs_grad_x, abs_grad_y;

    //X方向
    //Sobel(img, grad_x, CV_16S, 1, 0, 3, 1, 1, BORDER_DEFAULT);
    //convertScaleAbs(grad_x, abs_grad_x);
    Sobel(img, img, CV_16S, 1, 0, 3, 1, 1, BORDER_DEFAULT);
    convertScaleAbs(img, out);

    //Y方向
    //Sobel(img, grad_y, CV_16S, 0, 1, 3, 1, 1, BORDER_DEFAULT);
    //convertScaleAbs(grad_y, abs_grad_y);
    //convertScaleAbs(img, out);

    //合并
    //addWeighted(abs_grad_x, 0.5, abs_grad_y, 0.5, 0, out);

    return out;
}

Mat TwoValued(Mat &img) {
    Mat out;
   // threshold(img, out, 0, 255, CV_THRESH_OTSU + CV_THRESH_BINARY);
	threshold(img, out, 0, 255, THRESH_OTSU + THRESH_BINARY);
    //threshold(img, out, 100, 255, CV_THRESH_BINARY);

    return out;
}

Mat Close(Mat &img) {
    Mat out;
    //Mat element(5, 5, CV_8U, cv::Scalar(1));
    Mat element = getStructuringElement(MORPH_RECT, Size(17, 5));
    morphologyEx(img, out, cv::MORPH_CLOSE, element);

    return out;
}


void Contour(Mat &img, Mat &out) {
    RNG rng(12345);

    vector< Mat > contours(1000);
    vector<Vec4i> hierarchy(1000);
   // findContours(img, contours, hierarchy, CV_RETR_TREE, CV_CHAIN_APPROX_SIMPLE, Point(0, 0));
	findContours(img, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0));

    vector< Mat >::iterator itc = contours.begin();
    vector<RotatedRect> rects;
    int t = 0;
    while (itc != contours.end()) {
        //Create bounding rect of object
        RotatedRect mr = minAreaRect(Mat(*itc));
        //large the rect for more
        if (!verifySizes(mr)) {
            itc = contours.erase(itc);
        }
        else {
            ++itc;
            rects.push_back(mr);
        }
   }

    cv::Mat result;
    img.copyTo(result);
    for (int i = 0; i< contours.size(); i++)
    {
        drawContours(result, contours, i, Scalar(0, 0, 255), 2, 8, vector<Vec4i>(), 0, Point());
        //drawContours(result, contours, i, Scalar(255), 2);
    }
    //imshow("画轮廓", out);

    for (int i = 0; i < rects.size(); i++) {
        circle(result, rects[i].center, 3, Scalar(0, 255, 0), -1);

        float minSize = (rects[i].size.width < rects[i].size.height) ? rects[i].size.width : rects[i].size.height;
        //minSize = minSize - minSize*0.5;

        srand(time(NULL));
        Mat mask;
        mask.create(out.rows + 2, out.cols + 2, CV_8UC1);
        mask = Scalar::all(0);
        int loDiff = 30;
        int upDiff = 30;
        int connectivity = 4;
        int newMaskVal = 255;
        int NumSeeds = 10;
        Rect ccomp;
        //int flags = connectivity + (newMaskVal << 8) + CV_FLOODFILL_FIXED_RANGE + CV_FLOODFILL_MASK_ONLY;
		int flags = connectivity + (newMaskVal << 8) + FLOODFILL_FIXED_RANGE + FLOODFILL_MASK_ONLY;
        for (int j = 0; j < NumSeeds; j++) {
            Point seed;
            seed.x = rects[i].center.x + rand() % (int)minSize - (minSize / 2);
            seed.y = rects[i].center.y + rand() % (int)minSize - (minSize / 2);
            circle(result, seed, 1, Scalar(0, 255, 255), -1);
            int area = floodFill(out, mask, seed, Scalar(255, 0, 0), &ccomp, Scalar(loDiff, loDiff, loDiff), Scalar(upDiff, upDiff, upDiff), flags);
        }
        imshow("漫水填充", mask);

        vector<Point> pointsInterest;
        Mat_<uchar>::iterator itMask = mask.begin<uchar>();
        Mat_<uchar>::iterator end = mask.end<uchar>();
        for (; itMask != end; ++itMask)
            if (*itMask == 255)
                pointsInterest.push_back(itMask.pos());

        RotatedRect minRect = minAreaRect(pointsInterest);

        if (verifySizes(minRect)) {
            // rotated rectangle drawing
            Point2f rect_points[4]; minRect.points(rect_points);
            for (int j = 0; j < 4; j++)
                line(result, rect_points[j], rect_points[(j + 1) % 4], Scalar(0, 0, 255), 1, 8);

            //Get rotation matrix
            float r = (float)minRect.size.width / (float)minRect.size.height;
            float angle = minRect.angle;
            if (r < 1)
                angle = 90 + angle;
            Mat rotmat = getRotationMatrix2D(minRect.center, angle, 1);

            //Create and rotate image
            Mat img_rotated;
            //warpAffine(out, img_rotated, rotmat, out.size(), CV_INTER_CUBIC);//实现旋转
			warpAffine(out, img_rotated, rotmat, out.size(), INTER_CUBIC);//实现旋转

            //Crop image
            Size rect_size = minRect.size;
            if (r < 1)
                swap(rect_size.width, rect_size.height);
            Mat img_crop;
            getRectSubPix(img_rotated, rect_size, minRect.center, img_crop);

            Mat resultResized;
            resultResized.create(33, 144, CV_8UC3);
            resize(img_crop, resultResized, resultResized.size(), 0, 0, INTER_CUBIC);;

            ////Equalize croped image
            Mat grayResult;
            //cvtColor(resultResized, grayResult, CV_BGR2GRAY);// CV_RGB2GRAY
			cvtColor(resultResized, grayResult, COLOR_BGR2GRAY);// CV_RGB2GRAY
            blur(grayResult, grayResult, Size(3, 3));
            grayResult = histeq(grayResult);

            if (1) {
                stringstream ss(stringstream::in | stringstream::out);
                ss << "haha" << "_" << i << ".jpg";
                imwrite(ss.str(), grayResult);
            }

        }
    }
}


int readcar(const char*filename) {
    Mat img;
    Mat out;
    //Mat result;

    //载入图片
	if(filename){
		img = imread(filename);
	}else{
		img = imread("test1.jpg");//, CV_LOAD_IMAGE_GRAYSCALE);
	}
    img.copyTo(out);
    //imshow ("原始图", img);
    img = Gaussian(img);
    imshow ("高斯模糊", img);

    img = Grayscale(img);
    imshow("灰度化", img);

    img = Sobel(img);
    imshow("Sobel_X", img);

    img = TwoValued(img);
    imshow("二值化", img);

    img = Close(img);
    imshow("闭操作", img);
	Mat img32;
	img.convertTo(img32,CV_32S);
    //Contour(img32, out);

    waitKey(0);
    //cvDestroyAllWindows();
	return 0;
}